Step Functionのパラメータに「LambdaでDynamoDBから取得した数値」を使うときはint型に変換しよう
Step Functionsを使うとき、Lambdaで取得したパラメータに従って制御することがあります。
たとえば、Choice
で分岐させたり、Wait
を使ったりです。
そんなStep Functionsですが、「DynamoDBから取得した数値」をそのまま使うとエラーになっちゃった話です。
エラーになったこと
次のようなステートマシンがあります。最初のInit
で動くLambdaが「DynamoDBから数値を取得」し、その数値を用いてWait
させています。
このとき、DynamoDBから取得した数値をそのまま使うと、Step Functionsがエラーで失敗したのです。
実行イベント履歴のログはこちら。
{ "error": "States.Runtime", "cause": "An error occurred while executing the state 'Wait' (entered at the event id #7). The SecondsPath parameter cannot be parsed as a long value: $.waitSeconds == 10.0" }
エラーの原因
上記に書いている通り、StepFunctionのSecondsPath
でlong型が使えないためです。
「long型? どこから?」
「あなたのlong型はDynamoDBから。」
そう、DynamoDBのNumberを取得したとき、PythonではDecimal型として扱われるのです。
Lambdaコード
import boto3 import json import os dynamodb = boto3.resource('dynamodb') table_name = os.environ['TABLE_NAME'] def lambda_handler(event, context): table = dynamodb.Table(table_name) res = table.get_item(Key={ 'userId': '1234' }) wait_seconds = res['Item']['waitSeconds'] print(wait_seconds) print(type(wait_seconds)) return { 'waitSeconds': wait_seconds }
AWS SAM定義
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: StepFunctionsSample Resources: # StepFunctionsから起動されるLambda SfnInitFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.7 Timeout: 5 Environment: Variables: TABLE_NAME: !Ref ParameterTable Policies: - arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess SfnInitFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${SfnInitFunction} # 設定値が格納されているDynamoDBテーブル ParameterTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: userId AttributeType: S KeySchema: - AttributeName: userId KeyType: HASH BillingMode: PAY_PER_REQUEST # StepFunctionsのステートマシン SampleStateMachine: Type: AWS::Serverless::StateMachine Properties: Name: Sample-State-Machine Definition: StartAt: Init States: Init: Type: Task Resource: !GetAtt SfnInitFunction.Arn Next: Wait Wait: Type: Wait SecondsPath: $.waitSeconds End: true Role: !GetAtt SampleStateMachineRole.Arn # StepFunction用のIAMロール SampleStateMachineRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: !Sub states.${AWS::Region}.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
DynamoDBの様子
{ "userId": "1234", "waitSeconds": 10 }
対応策
簡単です。int型に直しましょう。
import boto3 import json import os dynamodb = boto3.resource('dynamodb') table_name = os.environ['TABLE_NAME'] def lambda_handler(event, context): table = dynamodb.Table(table_name) res = table.get_item(Key={ 'userId': '1234' }) wait_seconds = res['Item']['waitSeconds'] print(wait_seconds) print(type(wait_seconds)) return { 'waitSeconds': int(wait_seconds) }
無事に動きました。
さいごに
地味にハマるポイントでした。どなたかの参考になれば幸いです。